{
 "cells": [
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "---\n",
    "title: '`@unroll`'\n",
    "description: Unrolls a loop at compile time.\n",
    "css: /static/styles/page-navigation.css\n",
    "website:\n",
    "  open-graph:\n",
    "    image: /static/images/mojo-social-card.png\n",
    "  twitter-card:\n",
    "    image: /static/images/mojo-social-card.png\n",
    "---"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can add the `@unroll` decorator on any loop (such as `for` and `while`) to\n",
    "make the Mojo compiler [unroll the\n",
    "loop](https://en.wikipedia.org/wiki/Loop_unrolling), either fully or with a\n",
    "given unroll factor.\n",
    "\n",
    "For example, the compiler will unroll all 10 iterations of the following loop\n",
    "into 10 consecutive calls to `print()` (removing the `for` loop entirely):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@unroll\n",
    "for i in range(10):\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The decorator also accepts an \"unroll factor\" argument, which specifies how\n",
    "many iterations to unroll at once. For example, the unroll above is equivalent\n",
    "to `@unroll(10)` because it unrolls all iterations of the loop. So if you pass\n",
    "a number smaller than the loop bounds, the compiler creates multiple unrolls.\n",
    "For example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Unroll every 2 iterations, leaving a loop with 5 iterations.\n",
    "@unroll(2)\n",
    "for i in range (10):\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The result is equivalent to this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for i in range(0, 10, 2):\n",
    "    print(i)\n",
    "    print(i+1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "However, the compiler can unroll a loop only when the following statements are\n",
    "true:\n",
    "\n",
    "- The loop's lower bound, upper bound, and induction step size are compile-time\n",
    "constants (they do not vary at runtime). For example, in the above code\n",
    "`range(0, 10, 2)`, `0` is the lower bound, `10` is the upper bound, and `2`\n",
    "is the induction step size—these could instead be defined with variable names,\n",
    "but the values cannot vary at runtime.\n",
    "\n",
    "- Likewise, there are no early exits in the loop that make the loop count\n",
    "variable at runtime."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Compared to `unroll()`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The Mojo standard library also includes a function called\n",
    "[`unroll()`](/mojo/stdlib/algorithm/functional.html#unroll) that unrolls a\n",
    "given function that you want to call repeatedly, but has some important\n",
    "differences when compared to the `@unroll` decorator:\n",
    "\n",
    "- The `@unroll` decorator operates on loop expressions only, not on functions\n",
    "  like the `unroll()` function does.\n",
    "\n",
    "- The `@unroll` decorator determines how to unroll the loop based on the\n",
    "  induction variable (`i`), the value of which _is not_ known when compilation\n",
    "  begins. Whereas, the `unroll()` function calls upon your looping function\n",
    "  (`func`) with the `Int` loop index parameter that _is_ known at compile time.\n",
    "\n",
    "  This means two things:\n",
    "\n",
    "  - Within a loop using the `@unroll` decorator, the `i` induction variable is \n",
    "    still a runtime variable, so you _cannot_ use it as a parameter value (such\n",
    "    as for `SIMD[Int8, i]`). Whereas, within the `func` callback used with the\n",
    "    `unroll()` function, the `Int` loop index is known at compile time, so you\n",
    "    _can_ use it as a parameter value.\n",
    "\n",
    "  - The `unroll()` function unrolls at the beginning of compilation, which\n",
    "    might explode the program size that still needs to be compiled, depending\n",
    "    on the amount of code that's unrolled. Whereas, the `@unroll` decorator\n",
    "    performs unrolling later in the compilation, after the compiler is able to\n",
    "    evaluate the induction variable (`i`), which avoids early explosion of the\n",
    "    program size that still needs compilation."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Mojo",
   "language": "mojo",
   "name": "mojo-jupyter-kernel"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "mojo"
   },
   "file_extension": ".mojo",
   "mimetype": "text/x-mojo",
   "name": "mojo"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
